package cn.xnatural.app.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 线程不安全
 */
public class Cron {
    private static final Logger log = LoggerFactory.getLogger(Cron.class);
    final String exp; // 表达式
    final Second second;
    final Minute minute;
    final Hour hour;
    final Day day;
    final Month month;
    final Week week;
    final Year year;

    /**
     * 判断是否是工作日: 默认周一到周五
     */
    final Function<Calendar, Boolean> isWorkDay;

    Calendar _last; // 每次计算完成返回的值

    public Cron(String exp) {
        this(exp, cal -> {
            int week = cal.get(Calendar.DAY_OF_WEEK);
            return week >= Calendar.MONDAY && week <= Calendar.FRIDAY;
        });
    }
    public Cron(String exp, Function<Calendar, Boolean> isWorkDay) {
        if (exp == null || exp.isEmpty()) throw new IllegalArgumentException("exp isEmpty");
        this.exp = exp;
        String[] parts = exp.trim().split(" ");
        if (parts.length < 3) throw new IllegalArgumentException(exp + " At least 3 segments should be included");
        int i = 0;
        second = new Second(this, parts[i++].trim());
        minute = new Minute(this, parts[i++].trim());
        hour = new Hour(this, parts[i++].trim());
        day = new Day(this, parts.length >= 4 ? parts[i++].trim() : null);
        month = new Month(this, parts.length >= 5 ? parts[i++].trim() : null);
        week = new Week(this, parts.length >= 6 ? parts[i++].trim() : null);
        year = new Year(this, parts.length >= 7 ? parts[i++].trim() : null);
        this.isWorkDay = isWorkDay;
    }

    /**
     * 下次执行时间
     * @return null: 永不再执行
     */
    public Date next() {
        return next(_last == null ? new Date() : _last.getTime());
    }

    /**
     * 下次执行时间
     * @return null: 永不再执行
     */
    public Date next(Date d) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(d);
        _last = year.next(cal);
        year._origins.clear();
        month._origins.clear();
        week._origins.clear();
        day._origins.clear();
        hour._origins.clear();
        minute._origins.clear();
        second._origins.clear();
        return _last == null ? null : _last.getTime();
    }


    static class Part {
        protected final Cron cron;
        protected final String exp;
        protected Function<Calendar, Calendar> generate;
        // 记录每次进来的值
        final List<Calendar> _origins = new ArrayList<>(7);

        public Part(Cron cron, String exp) {
            this.cron = cron;
            this.exp = exp == null ? null : exp.trim();
            parse();
        }

        // 格式:  8,10-15,20-40/2~5,45/3
        // 格式:  8:30-17:30:30/30
        // 格式:  */5  每次计算时+5个单位
        // 格式:  *   每次计算时一个单位
        // 格式:  0~5   在一个范围内随机选择一个单位
        // 格式:  */25~30   在一个范围随机选择一个间隔
        void parse() {
            String prefix = "pattern_";
            generate = Stream.of(getClass().getSuperclass().getDeclaredMethods(), getClass().getDeclaredMethods())
                    .flatMap(Arrays::stream)
                    .filter(m -> m.getName().startsWith(prefix))
                    .filter(m -> Function.class.equals(m.getReturnType()))
                    .filter(m -> m.getParameterCount() == 0)
                    .sorted(Comparator.comparing(m -> m.getName().replace(prefix, "")))
                    .distinct()
                    .map(m -> {
                        try {
                            return (Function<Calendar, Calendar>) m.invoke(this);
                        } catch (Exception ex) {
                            if (ex instanceof InvocationTargetException && ex.getCause() instanceof IllegalArgumentException) {
                                throw (IllegalArgumentException) ex.getCause();
                            }
                            if (ex instanceof IllegalArgumentException) throw (IllegalArgumentException) ex;
                            throw new IllegalArgumentException("(" + cron.exp + ") " + getClass().getSimpleName() + " (" + exp + ") parse error", ex);
                        }
                    })
                    .filter(Objects::nonNull)
                    .findFirst()
                    .orElseThrow(() -> new IllegalArgumentException("(" + cron.exp + ") " + getClass().getSimpleName() + " (" + exp + ") format error"));
        }


        /**
         * 支持格式
         * <ul>
         *     <li>10-20</li>
         *     <li>0/5</li>
         *     <li>10-20,30-40</li>
         *     <li>10-20,25,30-40</li>
         *     <li>10-20,25,30-40/2</li>
         *     <li>10-20,25,30/2</li>
         * </ul>
         * @return 顺序的范围
         */
        protected List<Range> ranges(int min, int max) {
            String[] segs = exp.split(",");
            ArrayList<Range> ranges = new ArrayList<>();
            Pattern p = Pattern.compile("^(?<start>\\d{1,2})(-(?<end>\\d{1,2}))?(/(?<step>\\d{1,2}))?$");
            for (String seg : segs) {
                Matcher m = p.matcher(seg);
                if (!m.find()) return null;
                int start = Integer.parseInt(m.group("start"));
                String _end = m.group("end");
                Integer end = _end == null ? null : Integer.parseInt(_end);
                String _step = m.group("step");
                Integer step = _step == null ? null : Integer.parseInt(_step);
                if (start < min) throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+" (" + exp + ") start < "+min+" error");
                if (start > max) throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+" (" + exp + ") start > "+max+" error");
                if (end != null && end > max) throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+" (" + exp + ") end > "+max+" error");

                if (end == null && step == null) { // 0,5
                    ranges.add(new Range(start, start, null));
                } else if (end == null && step != null) { // 0/5
                    for (int i = start; i <= max; i += step) {
                        ranges.add(new Range(i, i, null));
                    }
                } else if (start > end) {
                    ranges.add(0, new Range(min, end, step));
                    ranges.add(new Range(start, max, step));
                } else {
                    ranges.add(new Range(start, end, step));
                }
            }
            for (int i = 0, size = ranges.size(); i < size; i++) {
                if (i + 1 >= size) break;
                if (ranges.get(i).end > ranges.get(i + 1).start) {
                    throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+" (" + exp + ") order error");
                }
            }
            ranges.trimToSize();
            return ranges;
        }


        /**
         * 格式例子:
         * <ul>
         *     <li>*</li>
         *     <li>?</li>
         * </ul>
         */
        Function<Calendar, Calendar> pattern_1() {
            if (exp == null || exp.isEmpty()) return cal -> cal;
            Matcher m = Pattern.compile("^[?*]$").matcher(exp);
            if (!m.find()) return null;
            return cal -> cal;
        }

        Calendar next(Calendar cal) {
            Calendar c = Calendar.getInstance();
            c.setTimeInMillis(cal.getTimeInMillis());
            _origins.add(c);
            return cal; 
        }
    }

    static class Second extends Part {
        public Second(Cron cron, String exp) {
            super(cron, exp);
        }

        /**
         * 格式例子:
         * <ul>
         *     <li>*</li>
         * </ul>
         */
        @Override
        Function<Calendar, Calendar> pattern_1() {
            Matcher m = Pattern.compile("^\\*$").matcher(exp);
            if (!m.find()) return null;
            return cal -> {
                // 第一次: 并且前边的段都没变化
                if (_origins.size() == 1 && _origins.get(0).compareTo(cron.year._origins.get(0)) == 0) {
                    cal.add(Calendar.SECOND, 1);
                }
                return cal;
            };
        }

        /**
         * 格式例子:
         * <ul>
         *     <li>20-40</li>
         *     <li>50-10</li>
         *     <li>10-20,25,30-40</li>
         *     <li>10-20,25,30-40/2</li>
         *     <li>10-20,25,30/2</li>
         * </ul>
         */
        Function<Calendar, Calendar> pattern_3() {
            List<Range> ranges = ranges(0, 59);
            if (ranges == null) return null;
            return new Function<Calendar, Calendar>() {
                @Override
                public Calendar apply(Calendar cal) {
                    int cur = cal.get(Calendar.SECOND);
                    for (Range range : ranges) {
                        if (cur < range.start) {
                            cal.set(Calendar.SECOND, range.start);
                            cal.set(Calendar.MILLISECOND, 0);
                            return cal;
                        }
                        if (cur <= range.end) {
                            if (_origins.size() == 1 && _origins.get(0).compareTo(cron.year._origins.get(0)) == 0) {
                                if (range.step == null) continue;
                                cal.add(Calendar.SECOND, range.step);
                                if (cal.get(Calendar.SECOND) > range.end) return apply(cal);
                            }
                            return cal;
                        }
                    }
                    cal.add(Calendar.MINUTE, 1);
                    cal.set(Calendar.SECOND, 0);
                    cal.set(Calendar.MILLISECOND, 0);
                    return cal;
                }
            };
        }


        /**
         * 格式例子:
         * <ul>
         *     <li>*\/5</li>
         * </ul>
         */
        Function<Calendar, Calendar> pattern_5() {
            Matcher m = Pattern.compile("^\\*/(?<step>\\d{1,2})$").matcher(exp);
            if (!m.find()) return null;
            int step = Integer.parseInt(m.group("step"));
            if (step < 2) throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+" (" + exp + ") step < 2 error");
            return cal -> {
                if (_origins.size() == 1 && _origins.get(0).compareTo(cal) == 0) {
                    cal.add(Calendar.SECOND, step);
                }
                return cal;
            };
        }

        Calendar next(Calendar cal) {
            super.next(cal);
            int curMinute = cal.get(Calendar.MINUTE);
            cal = generate.apply(cal);
            if (curMinute != cal.get(Calendar.MINUTE)) {
                return cron.hour.next(cal); // 秒改变了分钟那从小时开始重新计算
            }
            return cal;
        }
    }

    static class Minute extends Part {

        public Minute(Cron cron, String exp) {
            super(cron, exp);
        }



        /**
         * 格式例子:
         * {@link #ranges(int, int)}
         */
        Function<Calendar, Calendar> pattern_3() {
            List<Range> ranges = ranges(0, 59);
            if (ranges == null) return null;
            return new Function<Calendar, Calendar>() {
                @Override
                public Calendar apply(Calendar cal) {
                    int cur = cal.get(Calendar.MINUTE);
                    for (Range range : ranges) {
                        if (cur < range.start) {
                            cal.set(Calendar.MINUTE, range.start);
                            cal.set(Calendar.SECOND, 0);
                            cal.set(Calendar.MILLISECOND, 0);
                            return cal;
                        }
                        if (cur <= range.end) {
                            if (range.step == null) return cal;
                            if (_origins.size() == 1 && _origins.get(0).compareTo(cron.year._origins.get(0)) == 0) {
                                cal.add(Calendar.MINUTE, range.step);
                                if (cal.get(Calendar.MINUTE) > range.end) return apply(cal);
                            }
                            return cal;
                        }
                    }
                    cal.add(Calendar.HOUR_OF_DAY, 1);
                    cal.set(Calendar.MINUTE, 0);
                    cal.set(Calendar.SECOND, 0);
                    cal.set(Calendar.MILLISECOND, 0);
                    return cal;
                }
            };
        }


        /**
         * 格式例子:
         * <ul>
         *     <li>*\/5</li>
         * </ul>
         */
        Function<Calendar, Calendar> pattern_5() {
            Matcher m = Pattern.compile("^\\*/(?<step>\\d{1,2})$").matcher(exp);
            if (!m.find()) return null;
            int step = Integer.parseInt(m.group("step"));
            if (step < 2) throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+" (" + exp + ") step < 2 error");
            return cal -> {
                if (_origins.size() == 1 && _origins.get(0).compareTo(cal) == 0) {
                    cal.add(Calendar.MINUTE, step);
                }
                return cal;
            };
        }


        @Override
        Calendar next(Calendar cal) {
            super.next(cal);
            int curHour = cal.get(Calendar.HOUR_OF_DAY);
            cal = generate.apply(cal);
            if (cal.get(Calendar.HOUR_OF_DAY) != curHour) {
                return cron.hour.next(cal); // 分钟改变了小时那小时也得重新计算
            }
            return cron.second.next(cal);
        }
    }

    static class Hour extends Part {
        public Hour(Cron cron, String exp) {
            super(cron, exp);
        }


        /**
         * 格式例子:
         * {@link #ranges(int, int)}
         */
        Function<Calendar, Calendar> pattern_3() {
            List<Range> ranges = ranges(0, 23);
            if (ranges == null) return null;
            return new Function<Calendar, Calendar>() {
                @Override
                public Calendar apply(Calendar cal) {
                    int cur = cal.get(Calendar.HOUR_OF_DAY);
                    for (Range range : ranges) {
                        if (cur < range.start) {
                            cal.set(Calendar.HOUR_OF_DAY, range.start);
                            cal.set(Calendar.MINUTE, 0);
                            cal.set(Calendar.SECOND, 0);
                            cal.set(Calendar.MILLISECOND, 0);
                            return cal;
                        }
                        if (cur <= range.end) {
                            if (range.step == null) return cal;
                            if (_origins.size() == 1 && _origins.get(0).compareTo(cron.year._origins.get(0)) == 0) {
                                cal.add(Calendar.HOUR_OF_DAY, range.step);
                                if (cal.get(Calendar.HOUR_OF_DAY) > range.end) return apply(cal);
                            }
                            return cal;
                        }
                    }
                    cal.add(Calendar.DAY_OF_MONTH, 1);
                    cal.set(Calendar.HOUR_OF_DAY, 0);
                    cal.set(Calendar.MINUTE, 0);
                    cal.set(Calendar.SECOND, 0);
                    cal.set(Calendar.MILLISECOND, 0);
                    return cal;
                }
            };
        }

        /**
         * 格式例子:
         * <ul>
         *     <li>9-17</li>
         *     <li>8:30-17:50</li>
         *     <li>8:30-11:20,13:10-17:55,23:10-2</li>
         *     <li>20-6</li>
         * </ul>
         */
        Function<Calendar, Calendar> pattern_3_1() {
            Pattern pattern = Pattern.compile("^(?<startHour>\\d{1,2})(:(?<startMinute>\\d{1,2}))?-(?<endHour>\\d{1,2})(:(?<endMinute>\\d{1,2}))?$");
            String[] segs = exp.split(",");
            ArrayList<LTRange> ranges = new ArrayList<>();
            for (String seg : segs) {
                Matcher m = pattern.matcher(seg);
                if (!m.find()) return null;
                String _startMinute = m.group("startMinute");
                String _endMinute = m.group("endMinute");
                LocalTime startLt = LocalTime.of(Integer.parseInt(m.group("startHour")), _startMinute == null ? 0 : Integer.parseInt(_startMinute));
                LocalTime endLt = LocalTime.of(Integer.parseInt(m.group("endHour")), _endMinute == null ? 0 : Integer.parseInt(_endMinute));
                if (startLt.isAfter(endLt)) {
                    ranges.add(0, new LTRange(LocalTime.MIN, endLt));
                    ranges.add(new LTRange(startLt, LocalTime.MAX));
                } else {
                    ranges.add(new LTRange(startLt, endLt));
                }
            }
            for (int i = 0, size = ranges.size(); i < size; i++) {
                if (i + 1 >= size) break;
                if (ranges.get(i).start.isAfter(ranges.get(i + 1).start)) {
                    throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+" (" + exp + ") order error");
                }
            }
            ranges.trimToSize();

            return cal -> {
                int curHour = cal.get(Calendar.HOUR_OF_DAY);
                int curMinute = cal.get(Calendar.MINUTE);
                LocalTime curLt = LocalTime.of(curHour, curMinute);
                for (LTRange range : ranges) {
                    if (curLt.isBefore(range.start)) {
                        cal.set(Calendar.HOUR_OF_DAY, range.start.getHour());
                        cal.set(Calendar.MINUTE, range.start.getMinute());
                        cal.set(Calendar.SECOND, range.start.getSecond());
                        cal.set(Calendar.MILLISECOND, 0);
                    }
                    if (!curLt.isAfter(range.end)) {
                        return cal;
                    }
                }
                // 没找到范围
                cal.add(Calendar.DAY_OF_MONTH, 1);
                cal.set(Calendar.HOUR_OF_DAY, ranges.get(0).start.getHour());
                cal.set(Calendar.MINUTE, ranges.get(0).start.getMinute());
                cal.set(Calendar.SECOND, ranges.get(0).start.getSecond());
                cal.set(Calendar.MILLISECOND, 0);
                return cal;
            };
        }


        /**
         * 格式例子:
         * <ul>
         *     <li>*\/2</li>
         * </ul>
         */
        Function<Calendar, Calendar> pattern_5() {
            Matcher m = Pattern.compile("^\\*/(?<step>\\d{1,2})$").matcher(exp);
            if (!m.find()) return null;
            int step = Integer.parseInt(m.group("step"));
            if (step < 2) throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+" (" + exp + ") step < 2 error");
            return cal -> {
                if (_origins.size() == 1 && _origins.get(0).compareTo(cal) == 0) {
                    cal.add(Calendar.HOUR_OF_DAY, step);
                }
                return cal;
            };
        }


        @Override
        Calendar next(Calendar cal) {
            super.next(cal);
            int curDay = cal.get(Calendar.DAY_OF_MONTH);
            cal = generate.apply(cal);
            if (cal.get(Calendar.DAY_OF_MONTH) != curDay) {
                return cron.day.next(cal); // 小时改变了天那天也得重新计算
            }
            return cron.minute.next(cal);
        }
    }

    static class Day extends Part {
        public Day(Cron cron, String exp) {
            super(cron, exp);
        }
        Calendar fixed(Calendar cal, List<Integer> fixed) {
            int cur = cal.get(Calendar.DAY_OF_MONTH);
            if (cur < fixed.get(0)) {
                cal.set(Calendar.DAY_OF_MONTH, fixed.get(0));
                return cal;
            }
            if (cur > fixed.get(fixed.size() - 1)) {
                cal.add(Calendar.MONTH, 1);
                cal.set(Calendar.DAY_OF_MONTH, fixed.get(0));
                return cal;
            }
            for (Integer v : fixed) {
                if (v >= cur) {
                    cal.set(Calendar.DAY_OF_MONTH, v);
                    break;
                }
            }
            return cal;
        }


        /**
         * 格式例子:
         * <ul>
         *     <li>1,2,3</li>
         *     <li>15,L</li>
         *     <li>L</li>
         * </ul>
         */
        Function<Calendar, Calendar> pattern_2() {
            Matcher m = Pattern.compile("^(\\d{1,2}|L)((,\\d{1,2})*|,L)$").matcher(exp);
            if (!m.find()) return null;
            List<Integer> fixed = Arrays.stream(exp.split(","))
                    .filter(s -> !s.isEmpty())
                    .map(s -> s.equals("L") ? null : Integer.parseInt(s))
                    .filter(Objects::nonNull)
                    .distinct().collect(Collectors.toList());
            for (Integer v : fixed) {
                if (v > 31) throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+ " (" +exp+ ") fixed value > 31 error");
                if (v < 1) throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+ " (" +exp+ ") fixed value < 1 error");
            }
            boolean hasMaxDay = exp.contains("L");
            return cal -> {
                if (_origins.size() == 1 && _origins.get(0).compareTo(cal) == 0) {
                    if (hasMaxDay) {
                        List<Integer> newFixed = new ArrayList<>(fixed);
                        newFixed.add(cal.getActualMaximum(Calendar.DAY_OF_MONTH));
                        return fixed(cal, newFixed);
                    }
                    return fixed(cal, fixed);
                }
                return cal;
            };
        }



        /**
         * 格式例子:
         * {@link #ranges(int, int)}
         */
        Function<Calendar, Calendar> pattern_3() {
            List<Range> ranges = ranges(1, 31);
            if (ranges == null) return null;
            return new Function<Calendar, Calendar>() {
                @Override
                public Calendar apply(Calendar cal) {
                    int cur = cal.get(Calendar.DAY_OF_MONTH);
                    for (Range range : ranges) {
                        if (cur < range.start) {
                            if (range.start > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) break;
                            cal.set(Calendar.DAY_OF_MONTH, range.start);
                            cal.set(Calendar.HOUR_OF_DAY, 0);
                            cal.set(Calendar.MINUTE, 0);
                            cal.set(Calendar.SECOND, 0);
                            cal.set(Calendar.MILLISECOND, 0);
                            return cal;
                        }
                        if (cur <= range.end) {
                            if (range.step == null) return cal;
                            if (_origins.size() == 1 && _origins.get(0).compareTo(cron.year._origins.get(0)) == 0) {
                                cal.add(Calendar.DAY_OF_MONTH, range.step);
                                if (cal.get(Calendar.DAY_OF_MONTH) > range.end) return apply(cal);
                            }
                            return cal;
                        }
                    }
                    cal.add(Calendar.MONTH, 1);
                    cal.set(Calendar.DAY_OF_MONTH, 1);
                    cal.set(Calendar.HOUR_OF_DAY, 0);
                    cal.set(Calendar.MINUTE, 0);
                    cal.set(Calendar.SECOND, 0);
                    cal.set(Calendar.MILLISECOND, 0);
                    return cal;
                }
            };
        }


        /**
         * 格式例子:
         * <ul>
         *     <li>*\/2</li>
         * </ul>
         */
        Function<Calendar, Calendar> pattern_5() {
            Matcher m = Pattern.compile("^\\*/(?<step>\\d{1,2})$").matcher(exp);
            if (!m.find()) return null;
            int step = Integer.parseInt(m.group("step"));
            if (step < 2) throw new IllegalArgumentException("(" +cron.exp+ ") "+getClass().getSimpleName()+" (" + exp + ") step < 2 error");
            return cal -> {
                if (_origins.size() == 1 && _origins.get(0).compareTo(cal) == 0) {
                    cal.add(Calendar.DAY_OF_MONTH, step);
                }
                return cal;
            };
        }

        @Override
        Calendar next(Calendar cal) {
            super.next(cal);
            int curMonth = cal.get(Calendar.MONTH);
            cal = generate.apply(cal);
            if (cal.get(Calendar.MONTH) != curMonth) {
                return cron.month.next(cal); // 天改变了月那月也得重新计算
            }
            return cron.hour.next(cal);
        }
    }

    static class Month extends Part {
        public Month(Cron cron, String seg) {
            super(cron, seg);
        }


        /**
         * 格式例子:
         * {@link #ranges(int, int)}
         */
        Function<Calendar, Calendar> pattern_3() {
            List<Range> ranges = ranges(1, 11);
            if (ranges == null) return null;
            return new Function<Calendar, Calendar>() {
                @Override
                public Calendar apply(Calendar cal) {
                    int cur = cal.get(Calendar.MONTH);
                    for (Range range : ranges) {
                        if (cur < range.start) {
                            cal.set(Calendar.MONTH, range.start);
                            cal.set(Calendar.DAY_OF_MONTH, 1);
                            cal.set(Calendar.HOUR_OF_DAY, 0);
                            cal.set(Calendar.MINUTE, 0);
                            cal.set(Calendar.SECOND, 0);
                            cal.set(Calendar.MILLISECOND, 0);
                            return cal;
                        }
                        if (cur <= range.end) {
                            if (range.step == null) return cal;
                            if (_origins.size() == 1 && _origins.get(0).compareTo(cron.year._origins.get(0)) == 0) {
                                cal.add(Calendar.MONTH, range.step);
                                if (cal.get(Calendar.MONTH) > range.end) return apply(cal);
                            }
                            return cal;
                        }
                    }
                    cal.add(Calendar.YEAR, 1);
                    cal.set(Calendar.MONTH, 0);
                    cal.set(Calendar.DAY_OF_MONTH, 1);
                    cal.set(Calendar.HOUR_OF_DAY, 0);
                    cal.set(Calendar.MINUTE, 0);
                    cal.set(Calendar.SECOND, 0);
                    cal.set(Calendar.MILLISECOND, 0);
                    return cal;
                }
            };
        }


        @Override
        Calendar next(Calendar cal) {
            super.next(cal);
            int curYear = cal.get(Calendar.YEAR);
            cal = generate.apply(cal);
            if (cal.get(Calendar.YEAR) != curYear) {
                return cron.year.next(cal); // 月改变了年那年也得重新计算
            }
            return cron.week.next(cal);
        }
    }

    static class Week extends Part {
        public Week(Cron cron, String seg) {
            super(cron, seg);
        }


        /**
         * 格式例子:
         * {@link #ranges(int, int)}
         */
        Function<Calendar, Calendar> pattern_3() {
            List<Range> ranges = ranges(1, 6);
            if (ranges == null) return null;
            int[] arr1 = new int[]{0, 7, 1, 2, 3, 4, 5, 6};
            int[] arr2 = new int[]{0, 2, 3, 4, 5, 6, 7, 1};
            return new Function<Calendar, Calendar>() {
                @Override
                public Calendar apply(Calendar cal) {
                    int cur = arr1[cal.get(Calendar.DAY_OF_WEEK)];
                    for (Range range : ranges) {
                        if (cur < range.start) {
                            cal.set(Calendar.DAY_OF_WEEK, arr2[range.start]);
                            cal.set(Calendar.HOUR_OF_DAY, 0);
                            cal.set(Calendar.MINUTE, 0);
                            cal.set(Calendar.SECOND, 0);
                            cal.set(Calendar.MILLISECOND, 0);
                            return cal;
                        }
                        if (cur <= range.end) {
                            if (range.step == null) return cal;
                            if (_origins.size() == 1 && _origins.get(0).compareTo(cron.year._origins.get(0)) == 0) {
                                cal.add(Calendar.DAY_OF_WEEK, range.step);
                                if (arr1[cal.get(Calendar.DAY_OF_WEEK)] > range.end) return apply(cal);
                            }
                            return cal;
                        }
                    }
                    cal.add(Calendar.DAY_OF_MONTH, 1);
                    cal.set(Calendar.HOUR_OF_DAY, 0);
                    cal.set(Calendar.MINUTE, 0);
                    cal.set(Calendar.SECOND, 0);
                    cal.set(Calendar.MILLISECOND, 0);
                    return apply(cal);
                }
            };
        }

        @Override
        Calendar next(Calendar cal) {
            super.next(cal);
            int curMonth = cal.get(Calendar.MONTH);
            cal = generate.apply(cal);
            if (cal.get(Calendar.MONTH) != curMonth) {
                return cron.month.next(cal);
            }
            return cron.day.next(cal);
        }
    }

    static class Year extends Part {
        public Year(Cron cron, String exp) {
            super(cron, exp);
        }

        /**
         * 格式例子:
         * {@link #ranges(int, int)}
         */
        Function<Calendar, Calendar> pattern_3() {
            List<Range> ranges = ranges(0, 9999);
            if (ranges == null) return null;
            return new Function<Calendar, Calendar>() {
                @Override
                public Calendar apply(Calendar cal) {
                    int cur = cal.get(Calendar.YEAR);
                    for (Range range : ranges) {
                        if (cur < range.start) {
                            cal.set(Calendar.YEAR, range.start);
                            cal.set(Calendar.MONTH, 1);
                            cal.set(Calendar.DAY_OF_MONTH, 1);
                            cal.set(Calendar.HOUR_OF_DAY, 0);
                            cal.set(Calendar.MINUTE, 0);
                            cal.set(Calendar.SECOND, 0);
                            cal.set(Calendar.MILLISECOND, 0);
                            return cal;
                        }
                        if (cur <= range.end) {
                            if (range.step == null) return cal;
                            if (_origins.size() == 1 && _origins.get(0).compareTo(cal) == 0) {
                                cal.add(Calendar.YEAR, range.step);
                                if (cal.get(Calendar.YEAR) > range.end) return apply(cal);
                            }
                            return cal;
                        }
                    }
                    return null;
                }
            };
        }
        

        @Override
        Calendar next(Calendar cal) {
            super.next(cal);
            cal = generate.apply(cal);
            return cron.month.next(cal);
        }
    }

    static class Range {
        final int start, end;
        final Integer step;

        public Range(int start, int end, Integer step) {
            this.start = start;
            this.end = end;
            this.step = step;
        }

        @Override
        public String toString() {
            return "[" + start + "," + end + (step == null ? "" : ", " + step) +"]";
        }
    }

    static class LTRange {
        final LocalTime start, end;

        public LTRange(LocalTime start, LocalTime end) {
            this.start = start;
            this.end = end;
        }

        @Override
        public String toString() {
            return "[" + start + "," + end  +"]";
        }
    }
}